/*
 * Created by SharpDevelop.
 * User: Michael
 * Date: 19/11/2007
 * Time: 10:20 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections.Generic;

namespace SudokuSolver
{
	/// <summary>
	/// Description of .
	/// </summary>
	public class Colouring : Technique
	{
		public override bool Run(SudokuGame theGame)
		{
			bool changed = false;
			foreach(Group row in theGame.Rows)
			{
				if(FindColourPairs(row))
					changed = true;
			}
			
			foreach(Group column in theGame.Columns)
			{
				if(FindColourPairs(column))
					changed = true;
			}
			
			foreach(Group block in theGame.Blocks)
			{
				if(FindColourPairs(block))
					changed = true;
			}
			
			return changed;
		}
		
		private bool FindColourPairs(Group thisGroup)
		{
			List<int> missingNumbers = new List<int>();
			bool changed = false;
			for(int i = 1; i <= 9; i++)
			{
				if(thisGroup.MissingNumber(i))
				   missingNumbers.Add(i);
			}
			foreach(int missingNumber in missingNumbers)
			{
				//get the cells which can contain that number
				List<Cell> cellList = GetCellsThatCanContainX(missingNumber, thisGroup);
				if(cellList.Count == 2)
				{
					List<Cell> RedCells = new List<Cell>();
					List<Cell> GreenCells = new List<Cell>();
					RedCells.Add(cellList[0]);
					GreenCells.Add(cellList[1]);
					bool redfailed, greenfailed, addedSomething;
					do {
						redfailed = false;
						greenfailed = false;
						addedSomething = false;
						FindRedGreenCells(RedCells, ref redfailed, GreenCells, ref addedSomething, missingNumber);
						
						FindRedGreenCells(GreenCells, ref greenfailed, RedCells, ref addedSomething, missingNumber);
					} while(((!redfailed)&&(!greenfailed))&&(addedSomething));
					if(redfailed)
					{
						//all the greencells can be assigned the number
						//all the redcells must have the number removed from their candidate lists!
						changed = ExecuteChanges(GreenCells, RedCells, missingNumber);
					} else if(greenfailed)
					{
						//all the redcells can be assigned the number
						//all the greencells must have the number removed from their candidate lists!
						changed = ExecuteChanges(RedCells, GreenCells, missingNumber);
					}
				}
			}
			return changed;
		}
		
		private void FindRedGreenCells(List<Cell> thisColour, ref bool thisColourfailed, List<Cell> otherColour, ref bool added, int theNumber)
		{
			foreach(Cell cell in thisColour)
			{
				FindRedGreenCellsInGroup(thisColour, cell, ref thisColourfailed, otherColour, ref added, theNumber, cell.Row);
				FindRedGreenCellsInGroup(thisColour, cell, ref thisColourfailed, otherColour, ref added, theNumber, cell.Column);
				FindRedGreenCellsInGroup(thisColour, cell, ref thisColourfailed, otherColour, ref added, theNumber, cell.Block);
				if(thisColourfailed || added)
					return;
			}
		}
		
		private void FindRedGreenCellsInGroup(List<Cell> thisColour, Cell thisCell, ref bool thisColourfailed, List<Cell> otherColour, ref bool added, int theNumber, Group group)
		{
			//for every cell in the row
			foreach(Cell rowCell in group.Cells)
			{
				//that isn't this one
				if(rowCell != thisCell)
				{
					//and it can contain the missingNumber but isn't found
					if((!rowCell.Found) && (rowCell.IsPossible(theNumber)))
					{
						//if it's the same as this colour, this colour fails!
						if(thisColour.Contains(rowCell))
						{
							thisColourfailed = true;
							return;
						} //otherwise, if it's already a cell of the otherColour, do nothing
						else if (otherColour.Contains(rowCell))
						{
							//nothing!
						} //otherwise, it's neither red or green yet, colour it thiscolour!
						else {
							thisColour.Add(rowCell);
							//we added one!
							added = true;
						}
					}
				}
			}
		}
		
		private bool ExecuteChanges(List<Cell> winner, List<Cell> loser, int number)
		{
			bool changed = false;
			foreach(Cell winningCell in winner)
			{
				if(!winningCell.Found)
				{
					winningCell.Value = number;
					changed = true;
				}
			}
			foreach(Cell loserCell in loser)
			{
				if(loserCell.IsPossible(number))
				{
					loserCell.IsPossible(number, false);
					changed = true;
				}
			}
			return changed;
		}
		
		private List<Cell> GetCellsThatCanContainX(int x, Group group)
		{
			List<Cell> returnList = new List<Cell>();
			foreach(Cell cell in group.Cells)
			{
				if(cell.IsPossible(x))
					returnList.Add(cell);
			}
			return returnList;
		}
	}
}
